package com.example.supernote.ui.manager

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.example.supernote.data.entity.FileEntity
import com.example.supernote.data.entity.TrashEntity
import com.example.supernote.data.network.TextGenerationApi
import com.example.supernote.data.network.config.TextData
import com.example.supernote.data.network.config.TextGeneration
import com.example.supernote.data.network.config.TextPrompt
import com.example.supernote.data.network.config.generateAuthHeaders
import com.example.supernote.data.repository.FileEntityRepository
import com.example.supernote.model.manager.AbstractFileComposite
import com.example.supernote.model.manager.File
import com.example.supernote.model.manager.FileProperty
import com.example.supernote.model.manager.FileType
import com.example.supernote.model.manager.Folder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Date
import java.util.UUID
import kotlin.math.max

const val ROOT_PATH = "."

data class ManagerUiState(
    val itemDisplay: AbstractFileComposite = Folder(
        property = FileProperty(
            name = ROOT_PATH, type = FileType.ROOT, lastModified = Date().time
        )
    ),
    val isSorted: Boolean = false,
    val searchPattern: String = "",
    val fileEntityStream: Flow<List<FileEntity>>? = null
)

class ManagerViewModel(
    private val fileEntityRepository: FileEntityRepository
) : ViewModel() {
    private val _uiState =
        MutableStateFlow(ManagerUiState(fileEntityStream = fileEntityRepository.getAllFileEntitiesStream()))
    private val locFolder = HashMap<String, AbstractFileComposite>()
    val uiState: StateFlow<ManagerUiState> = _uiState.asStateFlow()


    private fun loadFiles(files: List<FileEntity>) {
        locFolder.clear()
        locFolder[ROOT_PATH] = Folder(
            property = FileProperty(
                name = ROOT_PATH, type = FileType.ROOT, lastModified = Date().time
            )
        )
        files.forEach { fileEntity ->
            val folderNames: List<String> = fileEntity.path.split("/")
            var parent: AbstractFileComposite = locFolder[ROOT_PATH]!!
            var absolutePath = ROOT_PATH
            folderNames.subList(1, folderNames.size).forEach { folderName ->
                absolutePath = "$absolutePath/$folderName"
                if (!locFolder.contains(absolutePath)) {
                    // load folder
                    val folder = Folder(
                        parent = parent, property = FileProperty(
                            name = folderName,
                            type = FileType.FOLDER,
                            lastModified = fileEntity.lastModified
                        )
                    )
                    parent.addChild(folder)
                    locFolder[absolutePath] = folder
                } else {
                    val folder = locFolder[absolutePath]!!
                    locFolder[absolutePath] = Folder(
                        parent = folder.parent, property = FileProperty(
                            name = folder.property!!.name,
                            type = folder.property.type,
                            lastModified = max(
                                fileEntity.lastModified, folder.property.lastModified
                            )
                        ), children = folder.children
                    )
                }
                parent = locFolder[absolutePath]!!
            }
            val file = File(
                parent = parent, property = FileProperty(
                    id = fileEntity.id,
                    name = fileEntity.name,
                    type = FileType.FILE,
                    lastModified = fileEntity.lastModified,
                    path = absolutePath
                )
            )
            parent.addChild(file)
        }
        locFolder.forEach { (f, s) ->
            println(f)
        }
        _uiState.update { it.copy(itemDisplay = getExistFolder(getAbsolutePath(uiState.value.itemDisplay)).copy()) }
    }

    private fun getExistFolder(absolutePath: String): AbstractFileComposite {
        return if (locFolder.contains(absolutePath)) {
            locFolder[absolutePath]!!
        } else {
            getExistFolder(absolutePath.substring(0, absolutePath.indexOfLast { it == '/' }))
        }
    }

    private fun getAbsolutePath(folder: AbstractFileComposite): String {
        return if (folder.parent == null) {
            folder.property!!.name
        } else {
            getAbsolutePath(folder.parent) + "/" + folder.property!!.name
        }
    }

    init {
        viewModelScope.launch {
            _uiState.value.fileEntityStream!!.collect {
                loadFiles(it)
            }
        }
    }

    fun search(searchPattern: String) {
        _uiState.update { it.copy(searchPattern = searchPattern) }
    }

    suspend fun deleteFile(file: AbstractFileComposite) {
        val parent = file.parent!!
        val ids = ArrayList<Int>()
        recursiveFindFile(ids, file)
        withContext(Dispatchers.IO) {
            for (id in ids) {
                val fileEntity = fileEntityRepository.getFileEntityStream(id)
                fileEntityRepository.insertTrashEntity(
                    TrashEntity(
                        id = fileEntity.id,
                        path = fileEntity.path,
                        name = fileEntity.name,
                        lastModified = fileEntity.lastModified,
                        text = fileEntity.text
                    )
                )
                fileEntityRepository.deleteFileEntityById(id)
            }
        }
        parent.children.remove(file)
        _uiState.update { it.copy(itemDisplay = parent.copy(), isSorted = false) }
    }

    private fun recursiveFindFile(ids: ArrayList<Int>, composite: AbstractFileComposite) {
        if (composite.property!!.type == FileType.FILE) {
            ids.add(composite.property.id)
        } else {
            for (child in composite.children) {
                recursiveFindFile(ids, child)
            }
        }
    }

    fun createFolder(folderName: String) {
        uiState.value.itemDisplay.addChild(
            Folder(
                parent = uiState.value.itemDisplay, property = FileProperty(
                    name = folderName, type = FileType.FOLDER, lastModified = Date().time
                )
            )
        )
        _uiState.update { it.copy(itemDisplay = it.itemDisplay.copy(), isSorted = false) }
    }

    suspend fun createFile(template: String, name: String) {
        fileEntityRepository.insertFileEntity(
            FileEntity(
                path = getAbsolutePath(uiState.value.itemDisplay),
                name = name,
                lastModified = Date().time,
                text = template
            )
        )
    }

    suspend fun generateNoteTemplateByPrompt(
        updateTemplate: (String) -> Unit,
        otherInfos: Map<String, String>
    ) {
        val requestId = UUID.randomUUID()
        var templateGenerated = ""
        val request = TextPrompt(
            prompt = otherInfos["prompt"]!!,
            systemPrompt = otherInfos["systemPrompt"]!!
        )
        var response = TextGeneration(
            code = -1, data = TextData(), msg = "网络错误，请重试"
        )
        try {
            response = TextGenerationApi.retrofitService.generateText(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = TextGenerationApi.GENERATE_TEXT_URI,
                    queryParams = "requestId=${requestId}"
                ), requestId = requestId.toString(), request.getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        templateGenerated = if (response.code == 0) response.data.content
        else response.msg
        updateTemplate(templateGenerated.trim())
    }

    fun sortFile() {
        _uiState.update { it.copy(isSorted = true) }
    }

    fun exportFile(file: AbstractFileComposite) {
        // TODO
    }

    fun forward(file: AbstractFileComposite) {
        _uiState.update { it.copy(itemDisplay = file, isSorted = false) }
    }

    fun backward() {
        _uiState.update {
            it.copy(
                itemDisplay = uiState.value.itemDisplay.parent!!, isSorted = false
            )
        }
    }


    private var _preClassifiedList: Map<String, MutableList<AbstractFileComposite>> = mutableMapOf()

    private suspend fun queryCategory(filename: String, systemPrompt: String): String {
        val requestId = UUID.randomUUID()
        val request = TextPrompt(
            prompt = filename,
            systemPrompt = systemPrompt
        )
        var response = TextGeneration(
            code = -1, data = TextData(), msg = "Network error"
        )
        try {
            response = TextGenerationApi.retrofitService.generateText(
                generateAuthHeaders(
                    appId = "3038973253",
                    appKey = "rFLkLVgmVydmTRfF",
                    method = "POST",
                    uri = TextGenerationApi.GENERATE_TEXT_URI,
                    queryParams = "requestId=${requestId}"
                ), requestId = requestId.toString(), request.getMap()
            )
        } catch (e: Exception) {
            println("#########MyException!!!!!!!!!!!!!!${e}")
        }
        val result = if (response.code == 0)
            response.data.content.trim()
        else
            "未知"
        println("###Output:$result->$filename")
        return result
    }

    suspend fun preClassified(
        unClassified: List<AbstractFileComposite>,
        systemPrompt: String,
        update: (Map<String, ArrayList<AbstractFileComposite>>) -> Unit
    ) {
        val preClassifiedList = mutableMapOf<String, ArrayList<AbstractFileComposite>>()
        unClassified.forEach {
            val category = queryCategory(it.property!!.name, systemPrompt)
            if (!preClassifiedList.containsKey(category))
                preClassifiedList[category] = ArrayList()
            preClassifiedList[category]?.add(it)
        }
        println("###Output:$preClassifiedList")
        _preClassifiedList = preClassifiedList
        update(preClassifiedList)
    }

    suspend fun autoClassified() {
        withContext(Dispatchers.IO) {
            for ((key, value) in _preClassifiedList) {
                val path = "./$key"
                for (file in value) {
                    val fileEntity = fileEntityRepository.getFileEntityStream(file.property!!.id)
                    fileEntityRepository.updateFileEntity(
                        FileEntity(
                            id = fileEntity.id,
                            path = path,
                            name = fileEntity.name,
                            lastModified = fileEntity.lastModified,
                            text = fileEntity.text
                        )
                    )
                }
            }
        }
        _uiState.value.fileEntityStream!!.collect {
            loadFiles(it)
        }
        _preClassifiedList = mutableMapOf()
    }

    suspend fun moveFilePath(path: String, file: AbstractFileComposite) {
        var prefix = ""
        if (!path.startsWith("./")) {
            prefix = if (path.startsWith("/")) {
                "."
            } else if (path == "") {
                ""
            }
            else {
                "./"
            }
        }
        withContext(Dispatchers.IO) {
            val fileEntity = fileEntityRepository.getFileEntityStream(file.property!!.id)
            fileEntityRepository.updateFileEntity(
                FileEntity(
                    id = fileEntity.id,
                    path = prefix + path,
                    name = fileEntity.name,
                    lastModified = fileEntity.lastModified,
                    text = fileEntity.text
                )
            )
        }
        _uiState.value.fileEntityStream!!.collect {
            loadFiles(it)
        }
    }

    /**
     * Factory for InterestsViewModel that takes PostsRepository as a dependency
     */
    companion object {
        fun provideFactory(
            fileEntityRepository: FileEntityRepository,
        ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return ManagerViewModel(fileEntityRepository) as T
            }
        }
    }
}